<?php
/**
 ***********************************************************************************************************************
 * 地理空间GEO服务
 * @author llq  2015/7/14  21:31
 ***********************************************************************************************************************
 */
namespace soa\core\map;
use soa\client;

/**
 * Class area
 * @package soa\core
 */
class geo extends client
{
    /**
     * 服务地址
     * @var string
     */
    public $service_url = 'http://core.kuaidihelp.com/';

    /**
     * 服务名称
     * @var string
     */
    protected $service_name = 'kuaidihelp_core';

    /**
     * 经纬度转换geo hash字符串
     * @param $lng
     * @param $lat
     * @return bool|string
     */
    public function location2hash($lng, $lat)
    {
        if(!empty($lng) && !empty($lat)) {
            return $this->get('v1/map/geo/location2hash', ['lng' => $lng, 'lat' => $lat]);
        }
        return '';
    }


    /**
     * 地址转换坐标。默认转换成国测局02年发布的坐标体系。又称“火星坐标”。
     * 如果转换源（高德、腾讯、搜狗等）转换失败或者超过频率限制，限制使用算法转换
     * @param $address
     * @return array|string
     */
    public function address2location($address)
    {
        if(empty($address) || !is_string($address)) {
            return [];
        }
        $geo = $this->address2amapLocation($address);
        //高德没定位成功时换百度地图定位
        if(!$geo) {
            $geo = $this->address2baiduLocation($address);
            if($geo) {
                $location = $this->baidu2gcj($geo['lng'], $geo['lat']);
                $geo['lng'] = $location['lng'];
                $geo['lat'] = $location['lat'];
                return $geo;
            }
            return null;
        }
        return $geo;
    }


    /**
     * @param $address
     * @return array|bool
     */
    public function address2baiduLocation($address)
    {
        if(empty($address) || !is_string($address)) {
            return [];
        }
        return $this->get('v1/map/geo/address2baiduLocation', ['address' => $address]);
    }

    /**
     * @param $address
     * @return array|bool
     */
    public function address2amapLocation($address)
    {
        if(empty($address) || !is_string($address)) {
            return [];
        }
        return $this->get('v1/map/geo/address2amapLocation', ['address' => $address]);
    }

    /**
     * @param $lng
     * @param $lat
     * @return array|bool
     */
    public function location2address($lng, $lat)
    {
        if(empty($lng) || empty($lat)) {
            return [];
        }
        $address = $this->location2amapAddress($lng, $lat);
        if(!$address) {
            return $this->location2baiduAddress($lng, $lat);
        }
        return $address;
    }


    /**
     * @param $lng
     * @param $lat
     * @return array|bool
     */
    public function location2baiduAddress($lng, $lat)
    {
        if(empty($lng) || empty($lat)) {
            return [];
        }

        return $this->get('v1/map/geo/location2baiduAddress', ['lng' => $lng, 'lat' => $lat]);
    }

    /**
     * @param $lng
     * @param $lat
     * @return array|bool
     */
    public function location2amapAddress($lng, $lat)
    {
        if(empty($lng) || empty($lat)) {
            return [];
        }
        return $this->get('v1/map/geo/location2amapAddress', ['lng' => $lng, 'lat' => $lat]);
    }

    /**
     * 百度坐标转高德坐标
     * @param $lat
     * @param $lng
     * @return array
     */
    public function baidu2gcj($lng, $lat)
    {
        $v = M_PI * 3000.0 / 180.0;
        $x = $lng - 0.0065;
        $y = $lat - 0.006;
        $z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $v);
        $t = atan2($y, $x) - 0.000003 * cos($x * $v);
        return array(
            'lat' => $z * sin($t),
            'lng' => $z * cos($t)
        );
    }

    /**
     * 高德坐标转百度坐标
     * @param $lat
     * @param $lng
     * @return array
     */
    public function gcj2baidu($lng, $lat)
    {
        $v = M_PI * 3000.0 / 180.0;
        $x = $lng;
        $y = $lat;
        $z = sqrt($x * $x + $y * $y) + 0.00002 * sin($y * $v);
        $theta = atan2($y, $x) + 0.000003 * cos($x * $v);
        $amap_lng = $z * cos($theta) + 0.0065;
        $amap_lat = $z * sin($theta) + 0.006;
        return array(
            'lat' => $amap_lat,
            'lng' => $amap_lng
        );
    }

    /**
     * 计算用户坐标和目标坐标的距离
     * @param $usr_lng
     * @param $usr_lat
     * @param $target_lng
     * @param $target_lat
     * @return float
     */
    public function distance($usr_lng, $usr_lat, $target_lng, $target_lat)
    {
        // 地球半径
        $R = 6378137;
        // 将角度转为狐度
        $radLat1 = deg2rad($usr_lat);
        $radLat2 = deg2rad($target_lat);
        $radLng1 = deg2rad($usr_lng);
        $radLng2 = deg2rad($target_lng);
        $x = cos($radLat1)*cos($radLat2)*cos($radLng1-$radLng2)+sin($radLat1)*sin($radLat2);
        $s = acos((int)$x == 1 ? 1 : $x)*$R;
        $s = round($s* 10000)/10000;
        return round($s);
    }


    /**
     * @param $result
     * @param string $field
     * @param string $order_by
     * @return mixed
     */
    public function sort($result, $field = 'distance', $order_by = 'asc')
    {
        $stack = array();
        array_push($stack, array(0, count($result) - 1));
        while (list($beg, $end) = array_pop($stack)) {
            if ($end > $beg) {
                $piv = $result[$beg][$field];
                $k = $beg + 1;
                $r = $end;
                while ($k < $r) {
                    if ($result[$k][$field] < $piv) {
                        $k++;
                    } else {
                        $tmp = $result[$k];
                        $result[$k] = $result[$r];
                        $result[$r] = $tmp;
                        $r--;
                    }
                }
                if ($result[$k][$field] >= $piv) {
                    $k--;
                }
                $tmp = $result[$k];
                $result[$k] = $result[$beg];
                $result[$beg] = $tmp;
                array_push($stack, array($beg, $k));
                array_push($stack, array($r, $end));
            }
        }
        return $order_by == 'asc' ? $result : array_reverse($result);
    }
}